/*
 * Copyright (c) 1999 2000
 * Information Promotion Agency of Japan (IPA),
 * Fujitsu Limited, and 
 * Matsuoka Laboratory, Tokyo Institute of Technology
 * All rights reserved.
 *
 * Redistribution and non-commercial use in source and binary forms, 
 * with or without modification, are permitted provided that the following 
 * conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * 3. All advertising materials mentioning features or use of this 
 *    software must display the following acknowledgement:
 *
 *    This product includes software developed by Information Promotion Agency
 *    of Japan, Fujitsu Limited, and Matsuoka Laboratory, 
 *    Tokyo Institute of Technology.
 *
 * 4. The names "Information Promotion Agency", "Fujitsu Limited",
 *    "Matsuoka Laboratory", or "Tokyo Institute of Technology" should
 *    not be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * 5. Any use of the source code or the binary in a commercial product, 
 *    whether may it be the origial representation or in some modified form,
 *    is not permitted without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $Revision: 1.41 $
 * $Date: 2000/02/23 08:43:32 $
 * $Author: kouya $
 */

#include "OpenJIT.h"
#include "org_OpenJIT_Compile.h"
#include "org_OpenJIT_ExceptionHandler.h"

extern void dispatchJVM();
extern void dispatchLazyNativeMethod();
extern void dispatchJITCompiler();

typedef Classorg_OpenJIT_Compile		ClassCompile;
typedef Classorg_OpenJIT_ExceptionHandler	ClassExceptionHandler;

#define COMPILE_CLASS	OPENJITPKG ARCH

#ifdef sparc
#include <signal.h>
typedef unsigned long * native_code_t;
#define BREAKPOINT_TRAP	kill(getpid(), SIGSTOP);
#define FLUSH_ICACHE(FROM, TO)						\
    {									\
	unsigned int addr;						\
	for (addr = (FROM); addr < (unsigned int)(TO); addr += 4) {	\
	    __asm__ volatile("iflush %0" : : "r" (addr));		\
	}								\
    }
#endif
#ifdef i386
typedef unsigned char * native_code_t;
#define BREAKPOINT_TRAP	__asm__ volatile("int $0x3")
#define FLUSH_ICACHE(FROM, TO) /* do nothing */
#endif

JITlink *jit_link;
ClassClass *classJavaLangObject;

static sys_mon_t *_compile_lock;
#if JDK_VERSION < 12
#define COMPILE_LOCK(ee)	sysMonitorEnter(_compile_lock)
#define COMPILE_UNLOCK(ee)	sysMonitorExit(_compile_lock)
#else
#define COMPILE_LOCK(ee)	sysMonitorEnter(EE2SysThread(ee), _compile_lock)
#define COMPILE_UNLOCK(ee)	sysMonitorExit(EE2SysThread(ee), _compile_lock)
#endif

static ClassClass *OpenJITClass;
static ClassClass *OpenJITExceptionClass;
struct methodblock *OpenJITcompileMB;

static int debug = 0;

/* Control the compilation */
static bool_t compiler_disabled = FALSE;

/* How many stack frames are available when self-compling. */
/* this variable is used for preventing from stack over flow */
/* or saving the memory for stack. */
static int max_stack_frames;

/* count the number of stack frames on current thread */
static int countStackFrames(ExecEnv *ee)
{
    JavaFrame *frame;
    int count = 0;

    for (frame = ee->current_frame; frame != 0; frame = frame->prev) {
	count++;
    }
    return count;
}

/* If a method does nothing, this function is called. */
/* It saves the memory area of native code to use common function. */
static void
nullMethod()
{
}

bool_t
OpenJIT_compile(ExecEnv *ee, struct methodblock *mb)
{
    JavaFrame *frame;
    HObject *obj;
    ClassCompile *compile;
    int i;
    void *old_invoker;
    void *old_dispatcher;
    bool_t success;

    if (compiler_disabled)
	return FALSE;

    if (!ENTRY_IS_FIXED(OpenJITcompileMB)) {
	/* on the way of self-compilation of OpenJIT */
	static ExecEnv *compile_ee = NULL;

	/* prevent from dead lock */
	if (compile_ee == NULL) {
	    /* first time */
	    compile_ee = ee;
	} else if (compile_ee != ee) {
	    /* another thread (e.g. Finalizer) */
	    /* In this case, dead lock might happen, so we invoke interpreter */
	    return FALSE;
	}

	/* check how many methods are now beeing compiled */
	if (max_stack_frames > 0 &&
	    countStackFrames(ee) > max_stack_frames)
	    return FALSE;
    }

    COMPILE_LOCK(ee);
    if (COMPILE_ON_THE_WAY(mb)) {
	/* now compling this method. avoid from double compiling */
	COMPILE_UNLOCK(ee);
	return FALSE;
    }
    START_COMPILE(mb);
    old_invoker = mb->invoker;
    old_dispatcher = mb->CompiledCode;
    /* reset invoker temporally */
    mb->invoker = (mb->fb.access & ACC_SYNCHRONIZED) ?
	invokeSynchronizedJavaMethod : invokeJavaMethod;
    /* reset dispatcher temporally */
    mb->CompiledCode = (void *)dispatchJVM;
    COMPILE_UNLOCK(ee);

    if (mb->code_length == 1 && mb->code[0] == 177) {
	/* this method does nothing. just return */
	mb->fb.access |= ACC_MACHINE_COMPILED;
	mb->CompiledCode = (void *)nullMethod;
	if (mb->CompiledCodeInfo != NULL)
	    sysFree(mb->CompiledCodeInfo);
	mb->CompiledCodeInfo = (void *)(8);
	mb->invoker = (void *)nullMethod;
	FIX_ENTRY(mb);
	return TRUE;
    }

    if ((obj = ObjAlloc(OpenJITClass, 0)) == 0)	/* OutOfMemoryError */
	goto fail_to_compile;
    KEEP_POINTER_ALIVE(unhand(obj));
    compile = (ClassCompile *)unhand(obj);
    compile->methodblock = (jint)mb;
    compile->constantpool = (jint)cbConstantPool(fieldclass(&mb->fb));
    compile->access = mb->fb.access;
    compile->nlocals = mb->nlocals;
    compile->maxstack = mb->maxstack;
    compile->args_size = mb->args_size;
    compile->inline_maxinsns = org_OpenJIT_Compile_DEFAULT_MAXINSNS;
    compile->clazz = fieldclass(&mb->fb);

    /* allocate method signature */
    {
	int len = strlen(fieldsig(&mb->fb));
	HArrayOfByte *arr = (HArrayOfByte *)ArrayAlloc(T_BYTE, len);

	if (arr == 0) /* OutOfMemoryError */
	    goto fail_to_compile;
	memcpy(unhand(arr)->body, fieldsig(&mb->fb), len);
	KEEP_POINTER_ALIVE(unhand(arr)->body);
	compile->signature = arr;
    }

    /* allocate bytecode */
    {
	HArrayOfByte *arr = (HArrayOfByte *)ArrayAlloc(T_BYTE, mb->code_length);
	if (arr == 0) /* OutOfMemoryError */
	    goto fail_to_compile;
	memcpy(unhand(arr)->body, mb->code, mb->code_length);
	KEEP_POINTER_ALIVE(unhand(arr)->body);
	compile->bytecode = arr;
	compile->bytecode_length = mb->code_length;
    }

    /* allocate exception table */
    compile->exceptionHandler = (HArrayOfObject *)ArrayAlloc(T_CLASS, mb->exception_table_length);
    if (compile->exceptionHandler == 0)	/* OutOfMemoryError */
	goto fail_to_compile;
    KEEP_POINTER_ALIVE(unhand(compile->exceptionHandler)->body);

    for (i = 0; i < mb->exception_table_length; i++) {
	HObject *xobj = ObjAlloc(OpenJITExceptionClass, 0);
	ClassExceptionHandler *handler;

	if (xobj == 0)	/* OutOfMemoryError */
	    goto fail_to_compile;
	(unhand(compile->exceptionHandler)->body)[i] = xobj;
	handler = (ClassExceptionHandler *)unhand(xobj);
	handler->startPC = (jint)mb->exception_table[i].start_pc;
	handler->endPC = (jint)mb->exception_table[i].end_pc;
	handler->handlerPC = (jint)mb->exception_table[i].handler_pc;
	handler->catchType = (jint)mb->exception_table[i].catchType;
    }

    /* attribute */
    {
	int len;
	HArrayOfByte *arr;

	if (mb->CompiledCodeInfo == NULL) {
	    len = 0;
	} else {
	    len = ((int *)(mb->CompiledCodeInfo))[0];
	}
	arr = (HArrayOfByte *)ArrayAlloc(T_BYTE, len);
	if (arr == 0) /* OutOfMemoryError */
	    goto fail_to_compile;
	if (mb->CompiledCodeInfo != NULL) {
	    memcpy(unhand(arr)->body, mb->CompiledCodeInfo + 4, len);
	    sysFree(mb->CompiledCodeInfo);
	}
	KEEP_POINTER_ALIVE(unhand(arr)->body);
	compile->attribute = arr;
    }

    if (debug & org_OpenJIT_Compile_DEBUG_MORE_VERBOSE) 
      jio_fprintf(stderr, "[Compiling: %s.%s%s]\n", 
	cbName(fieldclass(&mb->fb)), fieldname(&mb->fb), fieldsig(&mb->fb));

    frame = ee->current_frame;
    /* save Java Stack */
    frame->optop += mb->args_size;
    /* invoke compiler */
    success = do_execute_java_method_vararg(ee, obj, 0, 0, OpenJITcompileMB, FALSE, NULL, 0, FALSE);
    /* restore Java Stack */
    frame->optop -= mb->args_size;

    if (exceptionOccurred(ee)) {
      fail_to_compile:
	/* failed to compile due to exception. (ex. StackOverFlow) */
	if (debug & org_OpenJIT_Compile_DEBUG_VERBOSE)
		jio_fprintf(stderr, "[Uncompiled: %s.%s%s due to Exception]\n", 
			    cbName(fieldclass(&mb->fb)),
			    fieldname(&mb->fb),
			    fieldsig(&mb->fb));
	/* retry to compile later */
	COMPILE_LOCK(ee);
	END_COMPILE(mb);
	mb->invoker = old_invoker;
	mb->CompiledCode = old_dispatcher;
	COMPILE_UNLOCK(ee);
	exceptionClear(ee);
	return FALSE;
    }

    if (!success) {
	/* We didn't compile this method for the reasons of scope rule or CompileError. */
	exceptionClear(ee);
	/* never compile again */
	FIX_ENTRY(mb);
	return TRUE;
    }

    if (debug & org_OpenJIT_Compile_DEBUG_VERBOSE)
	  jio_fprintf(stderr, "[Compiled: 0x%x 0x%x %s.%s%s]\n", 
		(int)compile->code_area, 
		(int)((native_code_t)compile->code_area +compile->native_pc),
		cbName(fieldclass(&mb->fb)),
		fieldname(&mb->fb),
		fieldsig(&mb->fb));

    mb->fb.access |= ACC_MACHINE_COMPILED;
    /* set the entry point */
    mb->invoker = invokeCompiledCodeTable[RETURN_TYPE(mb)];
    mb->CompiledCode = (void *)compile->code_area;
    /* set the size of native code for debugging */
    mb->CompiledCodeInfo = (void *)(compile->native_pc);

    /* generate exception table for native code */
    {
	ClassExceptionHandler *handler;
	struct compiled_CatchFrame *cf;
	native_code_t code_area = (native_code_t)compile->code_area;
	cf = (struct compiled_CatchFrame *)(((int)(code_area +compile->native_pc) + 3) & ~3);

	for (i = 0; i < mb->exception_table_length; i++, cf++) {
	    handler = (ClassExceptionHandler *)unhand((unhand(compile->exceptionHandler)->body)[i]);
	    cf->start_pc = (caddr_t)(code_area + handler->startPC);
	    cf->end_pc = (caddr_t)(code_area + handler->endPC);
	    cf->handler_pc = (caddr_t)(code_area + handler->handlerPC);
	    mb->exception_table[i].compiled_CatchFrame = (struct CatchFrame *)cf;
	}
    }
    /* flush instruction cache. it depend on the target architecture */
    FLUSH_ICACHE(compile->code_area, (native_code_t)compile->code_area + compile->native_pc);
	
    exceptionClear(ee);
    FIX_ENTRY(mb);

    if (debug & org_OpenJIT_Compile_DEBUG_STOP_AT_INVOKE) {
	BREAKPOINT_TRAP;
    }
    return TRUE;
}

static bool_t
OpenJIT_invoke(JHandle *o, struct methodblock *mb, int args_size, ExecEnv *ee)
{
    if (OpenJIT_compile(ee, mb)) { /* success to compile */
	/* invoke compiled code */
	return mb->invoker(o, mb, args_size, ee);
    } else {
	/* invoke interpreter */
	if (mb->fb.access & ACC_SYNCHRONIZED)
	    return invokeSynchronizedJavaMethod(o, mb, args_size, ee);
	else
	    return invokeJavaMethod(o, mb, args_size, ee);
    }
}

static void 
OpenJIT_InitializeForCompiler(ClassClass *cb)
{
    int i;
    struct methodblock *mb = cbMethods(cb);
    char *p;

    for (i = cbMethodsCount(cb); --i >= 0; mb++) {
	/* abstract method can't be compiled */
	if (mb->fb.access & ACC_ABSTRACT)
	    continue;

	/* already compiled */
	if (mb->fb.access & ACC_MACHINE_COMPILED)
	    continue;

	mb->CompiledCodeInfo = NULL;

	/* class initializer should not be compiled */
	if (!strcmp(mb->fb.name, "<clinit>"))
	    continue;

	if (mb->fb.access & ACC_NATIVE) {
	    mb->CompiledCode = dispatchLazyNativeMethod;
	} else {
	    mb->invoker = OpenJIT_invoke;
	    mb->CompiledCode = dispatchJITCompiler;
	}

	/* set return type in CompiledCodeFlags */
	/* and clear other flags */
	for (p = mb->fb.signature; *p != SIGNATURE_ENDFUNC; p++)
	    ;
	switch(p[1]) {
	case SIGNATURE_VOID:
	    mb->CompiledCodeFlags = CCF_RETURN_VOID;
	    break;
	case SIGNATURE_LONG:
	    mb->CompiledCodeFlags = CCF_RETURN_LONG;
	    break;
	case SIGNATURE_DOUBLE:
	    mb->CompiledCodeFlags = CCF_RETURN_DOUBLE;
	    break;
	case SIGNATURE_FLOAT:
	    mb->CompiledCodeFlags = CCF_RETURN_FLOAT;
	    break;
	default:
	    mb->CompiledCodeFlags = CCF_RETURN_INT;
	    break;
	}
    }
}

#if JDK_VERSION < 12
static void
#else
static bool_t
#endif
OpenJIT_CompilerCompileClass(ClassClass *cb)
{
    int i;
    struct methodblock *mb = cbMethods(cb);
    ExecEnv *ee = EE();

    for (i = cbMethodsCount(cb); --i >= 0; mb++) {
	if ((mb->fb.access & (ACC_MACHINE_COMPILED|ACC_ABSTRACT|ACC_NATIVE)))
	    continue;
	OpenJIT_compile(ee, mb);
    }
#if JDK_VERSION >= 12
    return TRUE;
#endif
}

static void
OpenJIT_CompilerFreeClass(ClassClass *cb)
{
    int i;
    struct methodblock *mb = cbMethods(cb);
    
    for (i = cbMethodsCount(cb); --i >= 0; mb++) {
	if ((mb->fb.access & ACC_MACHINE_COMPILED) == 0)
	    continue;
	/* nullMethod must not freed */
	if (mb->code_length == 1)
	    continue;
#ifdef DEBUG
	jio_fprintf(stderr, "[Free Compiled Code: %s.%s%s]\n", 
		    cbName(fieldclass(&mb->fb)),
		    fieldname(&mb->fb),
		    fieldsig(&mb->fb));
#endif
	sysFree(mb->CompiledCode);
    }
}

static bool_t
OpenJIT_PCinCompiledCode(unsigned char *pc, struct methodblock *mb) { 
    return (bool_t)(((uint_t)pc - (uint_t)mb->CompiledCode) < (uint_t)mb->CompiledCodeInfo);
}

static unsigned char *
OpenJIT_CompiledCodePC(JavaFrame *frame, struct methodblock *mb) {
    return (unsigned char *)mb->CompiledCode;
}

static void
OpenJIT_CompilerEnable()
{
    compiler_disabled = FALSE;
}

static void
OpenJIT_CompilerDisable()
{
    compiler_disabled = TRUE;
}

static void
OpenJIT_ReadInCompiledCode(void *context, struct methodblock *mb, 
			int attribute_length, 
			uint32_t (*get1byte)(), 
			uint32_t (*get2bytes)(), 
			uint32_t (*get4bytes)(), 
			void (*getNbytes)()) 
{
    char *attr;
    attr = (char *)sysMalloc(attribute_length + 4);
    ((int *)attr)[0] = attribute_length;
    (*getNbytes)(context, attribute_length, attr + 4);
    mb->CompiledCodeInfo = attr;
}


static struct fieldblock *
findField(ExecEnv *ee, ClassClass *clazz, char *name, char *type)
{
    cp_item_type  constant_pool[6];
    unsigned char tt[6];	/* Type table */
    struct methodblock *mb = ee->current_frame->current_method;
    struct fieldblock *fb;

    constant_pool[CONSTANT_POOL_TYPE_TABLE_INDEX].type = tt;
    tt[0] = CONSTANT_POOL_ENTRY_RESOLVED;
    /* Field entry */
    constant_pool[1].i = 0x00020003;
    tt[1] = CONSTANT_Fieldref;
    /* Class entry */
    constant_pool[2].clazz = clazz;
    tt[2] = CONSTANT_POOL_ENTRY_RESOLVED | CONSTANT_Class;
    /* Name and Type entry */
    constant_pool[3].i = 0x00040005;
    tt[3] = CONSTANT_NameAndType;
    /* Method name and type  */
    constant_pool[4].cp = name;
    constant_pool[5].cp = type;
    tt[4] = tt[5] = CONSTANT_POOL_ENTRY_RESOLVED | CONSTANT_Utf8;

    mb = ee->current_frame->current_method;
    ee->current_frame->current_method = NULL;
    ResolveClassConstant(constant_pool, 1, ee, 1 << CONSTANT_Fieldref);
    if (exceptionOccurred(ee)) {
	exceptionClear(ee);
	return NULL;
    }
    fb = constant_pool[1].fb;
    ee->current_frame->current_method = mb;
    return fb;
}

void
java_lang_Compiler_start(JITlink *linkage)
{
    int i;
    ClassClass **pcb;
    ExecEnv *ee = EE();
    char *compiler;
    ClassClass *boot,*loader;

    /* Reloading java.lang.Compiler? */
    if (jit_link != NULL)
	return;

    jit_link = linkage;
    classJavaLangObject = *(linkage->classJavaLangObject);

#ifdef DEBUG
    jio_fprintf(stderr, "  " PACKAGE "-" VERSION " (%s JDK%s). Copyright (c) 1999,2000 The OpenJIT Team\n", ARCH, JDK_VERSION_STRING);
#endif

    _compile_lock = (sys_mon_t *) sysMalloc(sysMonitorSizeof());
    memset((char*) _compile_lock, 0, sysMonitorSizeof());
    monitorRegister(_compile_lock, "OpenJIT compile lock");

    boot = FindClass(ee, OPENJITPKG "OpenJITLoader", TRUE);
    if (boot == NULL) {
	loader = NULL;
    } else {
	loader = (ClassClass *)do_execute_java_method_vararg(ee, boot, "bootLoader", "()Ljava/lang/ClassLoader;", NULL, TRUE, NULL, 0, FALSE);
	if (loader != NULL) {
	    ee->current_frame->current_method->fb.clazz->obj->loader = (void *)loader;
	}
    }

    compiler = getenv("OPENJIT_COMPILER");
    if (compiler == NULL) {
	compiler = COMPILE_CLASS;
    }
    OpenJITClass = FindClass(ee, compiler, TRUE);
    if (OpenJITClass == 0) {
	jio_fprintf(stderr, "Can't find class %s\n", compiler);
	return;
    }
    CCSet(OpenJITClass, Sticky);
    OpenJITExceptionClass = FindClass(ee, OPENJITPKG "ExceptionHandler", TRUE);
    if (OpenJITExceptionClass == 0) {
	jio_fprintf(stderr, "Can't find class OpenJIT.ExceptionHandler\n");
	return;
    }
    CCSet(OpenJITExceptionClass, Sticky);
    /* find methodblock of "OpenJIT.Compile.compile()Z" */
    {
	cp_item_type  constant_pool[6];
	unsigned char tt[6];	/* Type table */

	constant_pool[CONSTANT_POOL_TYPE_TABLE_INDEX].type = tt;
	tt[0] = CONSTANT_POOL_ENTRY_RESOLVED;
	/* Method entry */
	constant_pool[1].i = 0x00020003;
	tt[1] = CONSTANT_Methodref;
	/* Class entry */
	constant_pool[2].clazz = OpenJITClass; 
	tt[2] = CONSTANT_POOL_ENTRY_RESOLVED | CONSTANT_Class;
	/* Name and Type entry */
	constant_pool[3].i = 0x00040005;
	tt[3] = CONSTANT_NameAndType;
	/* Method name and type  */
	constant_pool[4].cp = "compile";
	constant_pool[5].cp = "()Z";
	tt[4] = tt[5] = CONSTANT_POOL_ENTRY_RESOLVED | CONSTANT_Utf8;

	ResolveClassConstant(constant_pool, 1, ee, 1 << CONSTANT_Methodref);
	OpenJITcompileMB = constant_pool[1].mb;
    }
    /* find static fields in OpenJIT.Compile */
    {
	struct fieldblock *fb;
	HArrayOfInt *addr, *attr;

	static float fconst_0 = 0.0f;
	static float fconst_1 = 1.0f;
	static float fconst_2 = 2.0f;
	static double dconst_0 = 0.0;
	static double dconst_1 = 1.0;

	fb = findField(ee, OpenJITClass, "debug", "I");
	debug = fb->u.static_value;

	fb = findField(ee, OpenJITClass, "max_stack_frames", "I");
	max_stack_frames = fb->u.static_value;

	fb = findField(ee, OpenJITClass, "RT_fconst_0", "I");
	fb->u.static_address = &fconst_0;
	fb = findField(ee, OpenJITClass, "RT_fconst_1", "I");
	fb->u.static_address = &fconst_1;
	fb = findField(ee, OpenJITClass, "RT_fconst_2", "I");
	fb->u.static_address = &fconst_2;
	fb = findField(ee, OpenJITClass, "RT_dconst_0", "I");
	fb->u.static_address = &dconst_0;
	fb = findField(ee, OpenJITClass, "RT_dconst_1", "I");
	fb->u.static_address = &dconst_1;

	fb = findField(ee, OpenJITClass, "RT_addr", "[I");
	addr = (HArrayOfInt *)fb->u.static_value;
	fb = findField(ee, OpenJITClass, "RT_attr", "[I");
	attr = (HArrayOfInt *)fb->u.static_value;

	/* initialize some hooks which depend on target machine. */
	/*   linkage->p_CompiledCodeSignalHandler */
	/*   linkage->p_CompiledFramePrev */
	/*   linkage->p_CompiledFrameID (from JDK1.2) */
	OpenJIT_runtime_init(linkage, (int *)unhand(addr)->body, (int *)unhand(attr)->body);
    }

    /* Downloaded code from network is untrusted. */
    /* So, native methods in OpenJIT can't be linked. */
    /* We link native methods beforehand to trick the JDK. */
    if (loader) {
	int i;
	ClassClass *clazz = fieldclass(&OpenJITcompileMB->fb);
	struct methodblock *mb = cbMethods(clazz);
	void *loader = cbLoader(OpenJITClass);

	cbLoader(clazz) = NULL;
	for (i = cbMethodsCount(clazz); --i >= 0; mb++) {
	    if (mb->fb.access & ACC_NATIVE) {
		(void)OpenJIT_resolveNativeMethod(mb);
	    }
	}
	cbLoader(clazz) = loader;
    }

    *linkage->p_InitializeForCompiler = OpenJIT_InitializeForCompiler;
    *linkage->p_CompilerFreeClass = OpenJIT_CompilerFreeClass;
    *linkage->p_CompilerCompileClass = OpenJIT_CompilerCompileClass;
    *linkage->p_CompilerCompileClasses = (void *)NULL;
    *linkage->p_PCinCompiledCode = OpenJIT_PCinCompiledCode;
    *linkage->p_CompiledCodePC = OpenJIT_CompiledCodePC;
    *linkage->p_CompilerEnable = OpenJIT_CompilerEnable;
    *linkage->p_CompilerDisable = OpenJIT_CompilerDisable;
    *linkage->p_ReadInCompiledCode = OpenJIT_ReadInCompiledCode;
    *linkage->p_CompiledCodeAttribute = "OpenJIT";

    if (debug & org_OpenJIT_Compile_DEBUG_STOP_AT_START) {
	BREAKPOINT_TRAP;
    }

    /* initialize already loaded classes */
    pcb =  (ClassClass **)(*linkage->binclasses);
    for (i = (int)(*linkage->nbinclasses); --i >= 0; pcb++) {
	ClassClass *cb = *pcb;
	OpenJIT_InitializeForCompiler(cb);
    }
}

#define CHECK_CONSTANTPOOL_BOUNDARY(compile, index)			\
    if ((unsigned int)index >= cbConstantPoolCount(compile->clazz)) {	\
	SignalError(0, OPENJITPKG "CompilerError",			\
		    "Constant pool index out of boundary");		\
	return 0;							\
    }

jint org_OpenJIT_Compile_ConstantPoolValue(struct Horg_OpenJIT_Compile *h_compile, jint index)
{
    ClassCompile *compile = unhand(h_compile);
    cp_item_type *constantpool = (cp_item_type *)compile->constantpool;

    CHECK_CONSTANTPOOL_BOUNDARY(compile, index);
    return constantpool[index].i;
}

jint
org_OpenJIT_Compile_ConstantPoolTypeTable(struct Horg_OpenJIT_Compile *h_compile, jint index)
{
    ClassCompile *compile = unhand(h_compile);
    cp_item_type *constantpool = (cp_item_type *)compile->constantpool;
    unsigned char *type_table = constantpool[CONSTANT_POOL_TYPE_TABLE_INDEX].p;
    unsigned key = constantpool[index].i;

    CHECK_CONSTANTPOOL_BOUNDARY(compile, index);
    if (!CONSTANT_POOL_TYPE_TABLE_IS_RESOLVED(type_table, index)) {
	/* force resolving constant pool if class is already initialized */
	/* it helps to reduce code size and aboid from overhead of dynamic resolving */
	unsigned classkey = key >> 16;
	unsigned type = CONSTANT_POOL_TYPE_TABLE_GET_TYPE(type_table,index);

	if (
#ifdef RESOLVE_STRING_BEFOREHAND
	    type == CONSTANT_String ||
#endif
	    CONSTANT_POOL_TYPE_TABLE_IS_RESOLVED(type_table, classkey)) {
	    ExecEnv *ee = EE();
#if JDK_VERSION < 12
	    ResolveClassConstant(constantpool, index, ee, 1 << type);
#else
	    /* Dirty hack */
	    /* JDK1.2 raises IllegalAccessError for the security. */
	    /* To prevent this, set current_method to NULL temporarily. */
	    struct methodblock *mb = ee->current_frame->current_method;
	    ee->current_frame->current_method = NULL;
	    ResolveClassConstant(constantpool, index, ee, 1 << type);
	    ee->current_frame->current_method = mb;
#endif
	    /* ignore exception. */
	    /* it must be catched in execution time but compile time */
	    exceptionClear(ee);
	}
    }
    return CONSTANT_POOL_TYPE_TABLE_GET(type_table, index);
}

static char *
getClassName(cp_item_type *constantpool, int index)
{
    unsigned char *type_table = constantpool[CONSTANT_POOL_TYPE_TABLE_INDEX].p;
    unsigned key = constantpool[index].i; 

    if (!CONSTANT_POOL_TYPE_TABLE_IS_RESOLVED(type_table, index)) {
	switch(type_table[index] & CONSTANT_POOL_ENTRY_TYPEMASK) {
	case CONSTANT_Methodref:
	case CONSTANT_InterfaceMethodref:
        case CONSTANT_Fieldref:
	    {
		unsigned classkey = key >> 16;
		unsigned namekey = constantpool[classkey].i & 0xFFFF;

		if (CONSTANT_POOL_TYPE_TABLE_IS_RESOLVED(type_table, classkey)) {
		    ClassClass *cb = constantpool[classkey].clazz;
		    return cbName(cb);
		} else {
		    return constantpool[namekey].cp;
		}
		break;
	    }
	default:
	    SignalError(0, OPENJITPKG "CompilerError", "getClassName");
	    return 0;
	}
    } else {
	return cbName(constantpool[index].fb->clazz);
    }
}

static char *
getName(cp_item_type *constantpool, int index)
{
    unsigned char *type_table = constantpool[CONSTANT_POOL_TYPE_TABLE_INDEX].p;
    unsigned key = constantpool[index].i; 

    if (!CONSTANT_POOL_TYPE_TABLE_IS_RESOLVED(type_table, index)) {
	switch(type_table[index] & CONSTANT_POOL_ENTRY_TYPEMASK) {
	case CONSTANT_Methodref:
	case CONSTANT_InterfaceMethodref:
        case CONSTANT_Fieldref:
	    {
		unsigned nametypekey = key & 0xFFFF;
		unsigned nametypeindex = (unsigned)constantpool[nametypekey].i >> 16;
#if JDK_VERSION < 12
		if (CONSTANT_POOL_TYPE_TABLE_IS_RESOLVED(type_table, nametypekey)) {
		    char *name;

		    NAMETYPEHASH_LOCK();
		    name = ID2Str(nameTypeHash, nametypeindex, 0);
		    NAMETYPEHASH_UNLOCK();
		    return name;
		} else {
		    return (constantpool[nametypeindex].cp);
		}
#else  /* JDK1.2 */
		return (constantpool[nametypeindex].cp);
#endif
	    }
	    break;
	default:
	    SignalError(0, OPENJITPKG "CompilerError", "getName");
	    return 0;
	}
    } else {
	return constantpool[index].fb->name;
    }
}

static char *
getDescriptor(cp_item_type *constantpool, int index)
{
    unsigned char *type_table = constantpool[CONSTANT_POOL_TYPE_TABLE_INDEX].p;
    unsigned key = constantpool[index].i; 

    if (!CONSTANT_POOL_TYPE_TABLE_IS_RESOLVED(type_table, index)) {
	switch(type_table[index] & CONSTANT_POOL_ENTRY_TYPEMASK) {
	case CONSTANT_Methodref:
	case CONSTANT_InterfaceMethodref:
        case CONSTANT_Fieldref:
	    {
		unsigned nametypekey = key & 0xFFFF;
		unsigned nametypeindex = constantpool[nametypekey].i;
		unsigned fieldtypeindex = nametypeindex & 0xFFFF;

#if JDK_VERSION < 12
		if (CONSTANT_POOL_TYPE_TABLE_IS_RESOLVED(type_table, nametypekey)) {
		    char *sig;

		    NAMETYPEHASH_LOCK();
		    sig = ID2Str(nameTypeHash, fieldtypeindex, 0);
		    NAMETYPEHASH_UNLOCK();
		    return sig;
		} else {
		    return constantpool[fieldtypeindex].cp;
		}
#else  /* JDK1.2 */
		return constantpool[fieldtypeindex].cp;
#endif
	    }
	default:
	    SignalError(0, OPENJITPKG "CompilerError", "getDescriptor");
	    return 0;
	}
    } else {
	return constantpool[index].fb->signature;
    }
}

/* XXX This version does not set frame in case of exception ... */

/* XXX XXX XXX
 * According to the JVM spec, we are not allowed to invoke the static
 * initializer for a class until the first time it is actually used.
 * Use of 'ResolveClassConstant' unfortunately forces the static
 * initializer to be invoked. If one uses 'ResolveClassConstant2' with a
 * fifth argument of 0, the initializer will *not* be invoked, however,
 * code must be generated to invoke the initializer at some point!
 */
#define RESOLVE_CLASS_CONST(CP, INDEX, TYPE)			     \
    do {								     \
	unsigned char *type_table = CP[CONSTANT_POOL_TYPE_TABLE_INDEX].type; \
	if (!CONSTANT_POOL_TYPE_TABLE_IS_RESOLVED(type_table, INDEX)) {	     \
            /* ResolveClassConstant2(CP, INDEX, EE(), 1 << TYPE, 0); */  \
	    ResolveClassConstant(CP, INDEX, EE(), 1 << TYPE); 	     \
	    if (exceptionOccurred(EE())) {				     \
              /* XXX JUST DIE HERE */ fprintf(stderr,"EXCEPTION!!");         \
              exit(-1);                                                      \
	    }								     \
	}								     \
    } while(0)

/* Used by inlining: Given a constantpool and an index into that pool,
 * resolve the Methodref at that index and return its methodblock (as a
 * Java 'int').
 */
void org_OpenJIT_Compile_resolveClass(struct Horg_OpenJIT_Compile *h_compile, jint index) {
  ClassCompile *compile = unhand(h_compile);
  cp_item_type *constantpool = (cp_item_type *)compile->constantpool;
  
  RESOLVE_CLASS_CONST(constantpool,
		      index,
		      CONSTANT_Methodref);
}

/* Used by inlining */
void org_OpenJIT_Compile_initParser(struct Horg_OpenJIT_Compile *h_compile, jint caller_cp, jint index) {
  ClassCompile *parser = unhand(h_compile);
  cp_item_type *constantpool = (cp_item_type *)caller_cp;
  struct methodblock *mb;
  int i;

  RESOLVE_CLASS_CONST(constantpool,
		      index,
		      CONSTANT_Methodref);
  mb = constantpool[index].mb;

  parser->methodblock = (jint)mb;
  parser->constantpool = (jint)cbConstantPool(fieldclass(&mb->fb));
  parser->access = mb->fb.access;
  parser->nlocals = mb->nlocals;
  parser->maxstack = mb->maxstack;
  parser->args_size = mb->args_size;
  parser->clazz = fieldclass(&mb->fb);
  
  {
    int len = strlen(fieldsig(&mb->fb));
    HArrayOfByte *arr = (HArrayOfByte *)ArrayAlloc(T_BYTE, len);
    if (arr == 0) {
      /* OutOfMemoryError */
      exceptionClear(EE());
      return;
    }
    memcpy(unhand(arr)->body, fieldsig(&mb->fb), len);
    KEEP_POINTER_ALIVE(unhand(arr)->body);
    parser->signature = arr;
  }
  
  {
    HArrayOfByte *arr = (HArrayOfByte *)ArrayAlloc(T_BYTE, mb->code_length);
    if (arr == 0) {
      /* OutOfMemoryError */
      exceptionClear(EE());
      return;
    }
    memcpy(unhand(arr)->body, mb->code, mb->code_length);
    KEEP_POINTER_ALIVE(unhand(arr)->body);
    parser->bytecode = arr;
    parser->bytecode_length = mb->code_length;
  }
  
  parser->exceptionHandler = (HArrayOfObject *)ArrayAlloc(T_CLASS, mb->exception_table_length);
  if (parser->exceptionHandler == 0) {
    /* OutOfMemoryError */
    exceptionClear(EE());
    return;
  }
  KEEP_POINTER_ALIVE(unhand(parser->exceptionHandler)->body);

  for (i = 0; i < mb->exception_table_length; i++) {
    HObject *xobj;
    ClassExceptionHandler *handler;

    if ((xobj = ObjAlloc(OpenJITExceptionClass, 0)) == 0) {
      exceptionClear(EE());
      return;
    }
    (unhand(parser->exceptionHandler)->body)[i] = xobj;
    handler = (ClassExceptionHandler *)unhand(xobj);
    handler->startPC = (jint)mb->exception_table[i].start_pc;
    handler->endPC = (jint)mb->exception_table[i].end_pc;
    handler->handlerPC = (jint)mb->exception_table[i].handler_pc;
    handler->catchType = (jint)mb->exception_table[i].catchType;
  }

/* XXX mdw: Don't need this, and mb->CompiledCodeInfo may be
 * compiled already anyway...
 */
#if 0
  /* attribute */
  {
    int len;
    HArrayOfByte *arr;
    
    if (mb->CompiledCodeInfo == NULL) {
      len = 0;
    } else {
      len = ((int *)(mb->CompiledCodeInfo))[0];
    }
    arr = (HArrayOfByte *)ArrayAlloc(T_BYTE, len);
    if (arr == 0) {
      /* OutOfMemoryError */
      exceptionClear(EE());
      return;
    }
    if (mb->CompiledCodeInfo != NULL) {
      memcpy(unhand(arr)->body, mb->CompiledCodeInfo + 4, len);
      sysFree(mb->CompiledCodeInfo);
    }
    KEEP_POINTER_ALIVE(unhand(arr)->body);
    parser->attribute = arr;
  }
#endif

  return;
  
}
  

struct HArrayOfByte *org_OpenJIT_Compile_ConstantPoolClass0(struct Horg_OpenJIT_Compile *h_compile, jint index)
{
    ClassCompile *compile = unhand(h_compile);
    cp_item_type *constantpool = (cp_item_type *)compile->constantpool;
    char *class = NULL;
    HArrayOfByte *str;
    int len;

    CHECK_CONSTANTPOOL_BOUNDARY(compile, index);
    class = getClassName(constantpool, index);
    len = strlen(class);
    str = (HArrayOfByte *) ArrayAlloc(T_BYTE, len);
    if (str == 0) {
        SignalError(0, JAVAPKG "OutOfMemoryError", 0);
        return 0;
    }
    memcpy(unhand(str)->body, class, len);
    return str;
}

struct HArrayOfByte *org_OpenJIT_Compile_ConstantPoolName0(struct Horg_OpenJIT_Compile *h_compile, jint index)
{
    ClassCompile *compile = unhand(h_compile);
    cp_item_type *constantpool = (cp_item_type *)compile->constantpool;
    char *name = NULL;
    HArrayOfByte *str;
    int len;

    CHECK_CONSTANTPOOL_BOUNDARY(compile, index);
    name = getName(constantpool, index);
    len = strlen(name);
    str = (HArrayOfByte *) ArrayAlloc(T_BYTE, len);
    if (str == 0) {
        SignalError(0, JAVAPKG "OutOfMemoryError", 0);
        return 0;
    }
    memcpy(unhand(str)->body, name, len);
    return str;
}

jint org_OpenJIT_Compile_ConstantPoolAccess(struct Horg_OpenJIT_Compile *h_compile, jint index) {
  ClassCompile *compile = unhand(h_compile);
  cp_item_type *constantpool = (cp_item_type *)compile->constantpool;
  CHECK_CONSTANTPOOL_BOUNDARY(compile, index);

  RESOLVE_CLASS_CONST(constantpool,
		      index,
		      CONSTANT_Methodref);
  
  return constantpool[index].fb->access;
}

HArrayOfByte *org_OpenJIT_Compile_ConstantPoolMethodDescriptor(struct Horg_OpenJIT_Compile *h_compile, jint index)
{
    ClassCompile *compile = unhand(h_compile);
    cp_item_type *constantpool = (cp_item_type *)compile->constantpool;
    char *sig = NULL;

    CHECK_CONSTANTPOOL_BOUNDARY(compile, index);
    sig = getDescriptor(constantpool, index);
    if (sig) {
	int len = strlen(sig);
	HArrayOfByte *arr = (HArrayOfByte *)ArrayAlloc(T_BYTE, len);
	char *p;
	p = unhand(arr)->body;
	memcpy(p, sig, len);
	KEEP_POINTER_ALIVE(p);
	return arr;
    } else {
	return NULL;
    }
}

char org_OpenJIT_Compile_ConstantPoolFieldDescriptor(struct Horg_OpenJIT_Compile *h_compile, jint index)
{
    ClassCompile *compile = unhand(h_compile);
    cp_item_type *constantpool = (cp_item_type *)compile->constantpool;
    char *sig = NULL;

    CHECK_CONSTANTPOOL_BOUNDARY(compile, index);
    sig = getDescriptor(constantpool, index);
    return sig[0];
}

jint org_OpenJIT_Compile_ConstantPoolFieldOffset(struct Horg_OpenJIT_Compile *h_compile, jint index)
{
    ClassCompile *compile = unhand(h_compile);
    cp_item_type *constantpool = (cp_item_type *)compile->constantpool;

    CHECK_CONSTANTPOOL_BOUNDARY(compile, index);
    if ((constantpool[index].fb->access & ACC_STATIC) != 0) {
        /* IncompatibleClassChangeError */
        return (jint)-1;
    }
    return (jint)(constantpool[index].fb->u.offset);
}

jint org_OpenJIT_Compile_ConstantPoolFieldAddress(struct Horg_OpenJIT_Compile *h_compile, jint index)
{
    ClassCompile *compile = unhand(h_compile);
    cp_item_type *constantpool = (cp_item_type *)compile->constantpool;
    char *sig;

    CHECK_CONSTANTPOOL_BOUNDARY(compile, index);
    sig = getDescriptor(constantpool, index);
    if (sig == NULL) {
	SignalError(0, OPENJITPKG "CompilerError", "wrong static field reference");
	return 0;
    }
    if ((constantpool[index].fb->access & ACC_STATIC) == 0) {
        /* IncompatibleClassChangeError */
        return (jint)-1;
    }
    if (sig[0] == SIGNATURE_LONG || sig[0] == SIGNATURE_DOUBLE) {
	return (jint)(constantpool[index].fb->u.static_address);
    } else {
	return (jint)(&(constantpool[index].fb->u.static_value));
    }
}

void org_OpenJIT_Compile_NativeCodeAlloc(struct Horg_OpenJIT_Compile *h_compile, jint size)
{
    ClassCompile *compile = unhand(h_compile);

    compile->code_size = size;
    compile->code_area = (jint)sysMalloc(size);
    if (compile->code_area == 0) {
	SignalError(0, OPENJITPKG "CompilerError", "Native code out of memory");
    }
}

jint org_OpenJIT_Compile_NativeCodeReAlloc(struct Horg_OpenJIT_Compile *h_compile, jint size)
{
    ClassCompile *compile = unhand(h_compile);
    int old_area = (int)compile->code_area;
    struct methodblock *mb = (struct methodblock *)compile->methodblock;
    
    compile->code_size = size;
    /* make room for compiled exception table */
    size += mb->exception_table_length * sizeof(struct compiled_CatchFrame);
#ifdef DEBUG_RELOCATE
    /* force to reallocate native code space */
    compile->code_area = (jint)sysMalloc(size);
    if (compile->code_area == 0) {
	SignalError(0, OPENJITPKG "CompilerError", "Native code out of memory");
    }
    memcpy((void *)compile->code_area, (void *)old_area, size);
    sysFree((void *)old_area);
#else
    /* Linux realloc never fail???*/
    compile->code_area = (jint)sysRealloc((void *)compile->code_area, size);
    if (compile->code_area == 0) {
	SignalError(0, OPENJITPKG "CompilerError", "Native code out of memory");
    }
#endif
    return (old_area - (int)compile->code_area);
}

void org_OpenJIT_Compile_setNativeCode(struct Horg_OpenJIT_Compile *h_compile, jint pc, jint code)
{
    ClassCompile *compile = unhand(h_compile);

    if ((unsigned int)(((native_code_t)0) + pc + 1) > (unsigned)compile->code_size) {
	SignalError(0, OPENJITPKG "CompilerError", "Native code area out of boundary");
	return;
    }
    ((native_code_t)compile->code_area)[pc] = code;
}

jint org_OpenJIT_Compile_getNativeCode(struct Horg_OpenJIT_Compile *h_compile, jint pc)
{
    ClassCompile *compile = unhand(h_compile);

    if ((unsigned int)(((native_code_t)0) + pc + 1) > (unsigned)compile->code_size) {
	SignalError(0, OPENJITPKG "CompilerError", "Native code area out of boundary");
	return 0;
    } 
    return ((native_code_t)compile->code_area)[pc];
}

struct HArrayOfByte *org_OpenJIT_Compile_MethodName0(struct Horg_OpenJIT_Compile *h_compile)
{
    ClassCompile *compile = unhand(h_compile);
    struct methodblock *mb = (struct methodblock *)compile->methodblock;
    HArrayOfByte *str;
    int len = strlen(mb->fb.name);

    str = (HArrayOfByte *) ArrayAlloc(T_BYTE, len);
    if (str == 0) {
        SignalError(0, JAVAPKG "OutOfMemoryError", 0);
        return 0;
    }
    memcpy(unhand(str)->body, mb->fb.name, len);
    return str;
}
